﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using Xoohoo.Infrastructure.FastReflectionLib;
using System.Globalization;
using System.Web;
using System.Linq;

namespace Xoohoo.Html
{
    public static class ValidationExtensions
    {
        #region Static Constructor

        /*
        private static MethodInvoker s_ValidationMessageHelperInvoker;
        static ValidationExtensions()
        {
            MethodInfo validationMessageMethodInfo = typeof(ValidationExtensions).GetMethod("ValidationMessageHelper", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            s_ValidationMessageHelperInvoker = new MethodInvoker(validationMessageMethodInfo);
        }
        */

        #endregion

        #region ValidationMessage

        public static MvcHtmlString ValidationMessageFor<TViewModel, TModel, TProperty>(this HtmlHelper<TViewModel> htmlHelper
            , TModel model, Expression<Func<TModel, TProperty>> expression)
        {
            return htmlHelper.ValidationMessageFor(model, expression, null);
        }
        public static MvcHtmlString ValidationMessageFor<TViewModel, TModel, TProperty>(this HtmlHelper<TViewModel> htmlHelper
            , TModel model, Expression<Func<TModel, TProperty>> expression
            , string validationMessage)
        {
            return htmlHelper.ValidationMessageFor(model, expression, validationMessage, null, null);
        }
        public static MvcHtmlString ValidationMessageFor<TViewModel, TModel, TProperty>(this HtmlHelper<TViewModel> htmlHelper
            , TModel model, Expression<Func<TModel, TProperty>> expression
            , string validationMessage
            , string correctMessage
            )
        {
            return htmlHelper.ValidationMessageFor(model, expression, validationMessage, correctMessage, null);
        }
        public static MvcHtmlString ValidationMessageFor<TViewModel, TModel, TProperty>(this HtmlHelper<TViewModel> htmlHelper
            , TModel model, Expression<Func<TModel, TProperty>> expression
            , string validationMessage
            , object htmlAttributes)
        {
            return htmlHelper.ValidationMessageFor(model, expression, validationMessage, null, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
        }
        public static MvcHtmlString ValidationMessageFor<TViewModel, TModel, TProperty>(this HtmlHelper<TViewModel> htmlHelper
            , TModel model, Expression<Func<TModel, TProperty>> expression
            , string validationMessage
            , string correctMessage
            , object htmlAttributes)
        {
            return htmlHelper.ValidationMessageFor(model, expression, validationMessage, correctMessage, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
        }
        public static MvcHtmlString ValidationMessageFor<TViewModel, TModel, TProperty>(this HtmlHelper<TViewModel> htmlHelper
            , TModel model, Expression<Func<TModel, TProperty>> expression
            , string validationMessage
            , string correctMessage
            , IDictionary<string, object> htmlAttributes)
        {
            ViewDataDictionary<TModel> viewData = new ViewDataDictionary<TModel>(model);
            /*
            return (MvcHtmlString)s_ValidationMessageHelperInvoker.Invoke(
                null
                , htmlHelper
                , ModelMetadata.FromLambdaExpression(expression, viewData)
                , ExpressionHelper.GetExpressionText(expression)
                , validationMessage
                , htmlAttributes);
             */
            return htmlHelper.ValidationMessageHelper(
                ModelMetadata.FromLambdaExpression(expression, viewData)
                , ExpressionHelper.GetExpressionText(expression)
                , validationMessage
                , correctMessage
                , htmlAttributes);
        }

        #endregion

        #region Private Static Methods

        private static MvcHtmlString ValidationMessageHelper(this HtmlHelper htmlHelper
            , ModelMetadata modelMetadata
            , string expression
            , string validationMessage
            , string correctMessage
            , IDictionary<string, object> htmlAttributes)
        {
            string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
            FormContext formContext = htmlHelper.ViewContext.GetFormContextForClientValidation();

            if (!htmlHelper.ViewData.ModelState.ContainsKey(modelName) && formContext == null)
            {
                return null;
            }

            ModelState modelState = htmlHelper.ViewData.ModelState[modelName];
            ModelErrorCollection modelErrors = (modelState == null) ? null : modelState.Errors;
            ModelError modelError = (((modelErrors == null) || (modelErrors.Count == 0)) ? null : modelErrors.FirstOrDefault(m => !String.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0]);

            if (modelError == null && formContext == null)
            {
                return null;
            }

            TagBuilder builder = new TagBuilder("span");
            builder.MergeAttributes(htmlAttributes);
            //Edited
            //builder.AddCssClass((modelError != null) ? HtmlHelper.ValidationMessageCssClassName : HtmlHelper.ValidationMessageValidCssClassName);
            builder.AddCssClass((modelError != null) ? HtmlHelper.ValidationMessageCssClassName : "field-validation-default");

            if (modelError != null)
            {
                //Edited
                //builder.SetInnerText(GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, modelState));
                builder.InnerHtml = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, modelState);
            }
            else if (!String.IsNullOrEmpty(validationMessage))
            {
                //Edited
                //builder.SetInnerText(validationMessage);
                builder.InnerHtml = validationMessage;
            }
            if (!String.IsNullOrEmpty(correctMessage))
                builder.MergeAttribute("data-valmsg-correct", correctMessage);

            if (formContext != null)
            {
                //Edited
                //bool replaceValidationMessageContents = String.IsNullOrEmpty(validationMessage);
                bool replaceValidationMessageContents = true;

                if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
                {
                    builder.MergeAttribute("data-valmsg-for", modelName);
                    builder.MergeAttribute("data-valmsg-replace", replaceValidationMessageContents.ToString().ToLowerInvariant());
                }
                else
                {
                    FieldValidationMetadata fieldMetadata = ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName);
                    // rules will already have been written to the metadata object
                    fieldMetadata.ReplaceValidationMessageContents = replaceValidationMessageContents; // only replace contents if no explicit message was specified

                    // client validation always requires an ID
                    builder.GenerateId(modelName + "_validationMessage");
                    fieldMetadata.ValidationMessageId = builder.Attributes["id"];
                }
            }
            //Edited
            //return builder.ToMvcHtmlString(TagRenderMode.Normal);
            return new MvcHtmlString(builder.ToString(TagRenderMode.Normal));
        }

        private static FormContext GetFormContextForClientValidation(this ViewContext viewContext)
        {
            if (!viewContext.ClientValidationEnabled)
            {
                return null;
            }
            return viewContext.FormContext;
        }
        private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState)
        {
            if (!String.IsNullOrEmpty(error.ErrorMessage))
            {
                return error.ErrorMessage;
            }
            if (modelState == null)
            {
                return null;
            }

            string attemptedValue = (modelState.Value != null) ? modelState.Value.AttemptedValue : null;
            //Edited
            //return String.Format(CultureInfo.CurrentCulture, GetInvalidPropertyValueResource(httpContext), attemptedValue);
            return String.Format(CultureInfo.CurrentCulture, "The value '{0}' is invalid.", attemptedValue);
        }
        private static FieldValidationMetadata ApplyFieldValidationMetadata(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string modelName)
        {
            FormContext formContext = htmlHelper.ViewContext.FormContext;
            FieldValidationMetadata fieldMetadata = formContext.GetValidationMetadataForField(modelName, true /* createIfNotFound */);

            // write rules to context object
            IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetadata, htmlHelper.ViewContext);
            foreach (ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules()))
            {
                fieldMetadata.ValidationRules.Add(rule);
            }

            return fieldMetadata;
        }

        #endregion
    }
}
